package util;

import common.StrSrcJavaObject;
import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Map;

/**
 * 反射工具
 * @author lijiancheng
 * @date 2021/7/12
 */
public class ReflactUtil {

    /**
     * --执行代码--
     * 只适用单行代码，可按行循环执行多行代码
     * 但该方法需提前在 params 中设置所有用到的变量
     * @param exp 代码
     * @param params 参数列表
     * @return 返回值
     */
    public Object convert(String exp, Map<String, Object> params) {
        JexlEngine engine = new JexlEngine();
        JexlContext context = new MapContext();
        Expression expression = engine.createExpression(exp);
        params.forEach(context::set);
        return expression.evaluate(context);
    }

    /**
     * 字符串 编译 成 字节码
     * @param className 类名
     * @param code 完整代码
     * @return Class对象
     */
    private Class<?> compile(String className, String code) {
        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, StandardCharsets.UTF_8);
        StrSrcJavaObject srcObject = new StrSrcJavaObject(className, code);
        Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(srcObject);
        String flag = "-d";
        String outDir = "";
        try {
            File classPath = new File(Thread.currentThread().getContextClassLoader().getResource("").toURI());
            outDir = classPath.getAbsolutePath() + File.separator;
        } catch (URISyntaxException e1) {
            e1.printStackTrace();
        }
        Iterable<String> options = Arrays.asList(flag, outDir);
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects);
        boolean result = task.call();
        if (result == true) {
            try {
                return Class.forName(className);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 执行
     * @param className 类名 (动态编译，项目中不应有该类)
     * @param code 完整代码 (从 package 开始)
     * @param param 参数
     * @return 返回值
     */
    public Object run(String className, String code, String param){

        // 拼接 包名 + 类名
        String classNameAbsolute = "temp." + className;
        Class<?> clazz = compile(classNameAbsolute, code);

        try {
            // 生成对象
            Object obj = clazz.newInstance();
            Class<? extends Object> cls = obj.getClass();
            // 调用sayHello方法
//            Method m = clazz.getMethod("sayHello",String[].class);
            Method m = clazz.getMethod("hello", String.class);
//            Object invoke = m.invoke(obj, new Object[] { new String[] {} });
            Object invoke = m.invoke(obj, param);
            return invoke;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }

}
